<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" >

<title>测试平台系列(8) 与前端联调注册/登录接口(part 1) | 小克的blog</title>

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
<link rel="shortcut icon" href="https://woodywrx.gitee.io/blog/favicon.ico?v=1615823433634">
<link rel="stylesheet" href="https://woodywrx.gitee.io/blog/styles/main.css">



<link rel="stylesheet" href="https://unpkg.com/aos@next/dist/aos.css" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>



    <meta name="description" content="与前端联调注册/登录接口(part 1)
前方高能
咱们今天讲的内容，可能和「前端」关联比较大，如果没有经验的同学，或者说只想专注后端的同学，你们直接copy前端的代码即可。
秉着毁人不倦的思想，笔者打算细讲一部分前端的内容，毕竟还有一部分..."/>
    <meta name="keywords" content="自动化测试平台"/>
</head>
<body>

<div id="app" class="main">

    <div class="sidebar" :class="{ 'full-height': menuVisible }">
  <div class="top-container" data-aos="fade-right">
    <div class="top-header-container">
      <a class="site-title-container" href="https://woodywrx.gitee.io/blog">
        <img src="https://woodywrx.gitee.io/blog/images/avatar.png?v=1615823433634" class="site-logo">
        <h1 class="site-title">小克的blog</h1>
      </a>
      <div class="menu-btn" @click="menuVisible = !menuVisible">
        <div class="line"></div>
      </div>
    </div>
    <div>
      
        
          <a href="https://woodywrx.gitee.io/blog" class="site-nav">
            首页
          </a>
        
      
        
          <a href="https://woodywrx.gitee.io/blog/tags" class="site-nav">
            标签
          </a>
        
      
        
          <a href="https://woodywrx.gitee.io/blog/post/about" class="site-nav">
            关于
          </a>
        
      
    </div>
  </div>
  <div class="bottom-container" data-aos="flip-up" data-aos-offset="0">
    <div class="social-container">
      
        
      
        
      
        
      
        
      
        
      
    </div>
    <div class="site-description">
      欢迎来到我的小窝~这里不仅有博客，也有日记。
    </div>
    <div class="site-footer">
      wuranxu's blog | <a class="rss" href="https://woodywrx.gitee.io/blog/atom.xml" target="_blank">RSS</a>
    </div>
  </div>
</div>


    <div class="main-container">
        <div class="content-container" data-aos="fade-up">
            <div class="post-detail">
                <h2 class="post-title">测试平台系列(8) 与前端联调注册/登录接口(part 1)</h2>
                <div class="post-date">2021-03-10 00:36:05</div>
                
                <div class="post-content" v-pre>
                    <section id="nice" data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px; padding: 0 10px; word-spacing: 0px; word-break: break-word; word-wrap: break-word; text-align: left; line-height: 1.25; color: #2b2b2b; font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light; letter-spacing: 2px; background-image: linear-gradient(90deg, rgba(50, 0, 0, 0.04) 3%, rgba(0, 0, 0, 0) 3%), linear-gradient(360deg, rgba(50, 0, 0, 0.04) 3%, rgba(0, 0, 0, 0) 3%); background-size: 20px 20px; background-position: center center;"><h2 data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; display: block; border-bottom: 4px solid #40B8FA;"><span class="prefix" style="display: flex; width: 20px; height: 20px; background-size: 20px 20px; background-image: url(https://files.mdnice.com/fullstack-1.png); margin-bottom: -22px;"></span><span class="content" style="display: flex; color: #40B8FA; font-size: 20px; margin-left: 25px;">与前端联调注册/登录接口(part 1)</span><span class="suffix" style="display: flex; box-sizing: border-box; width: 200px; height: 10px; border-top-left-radius: 20px; background: RGBA(64, 184, 250, .5); color: rgb(255, 255, 255); font-size: 16px; letter-spacing: 0.544px; justify-content: flex-end; float: right; margin-top: -10px; box-sizing: border-box !important; overflow-wrap: break-word !important;"></span></h2>
<h3 data-tool="mdnice编辑器" style="padding: 0px; color: black; font-size: 17px; font-weight: bold; text-align: center; position: relative; margin-top: 20px; margin-bottom: 20px;"><span class="prefix" style="display: none;"></span><span class="content" style="border-bottom: 2px solid RGBA(79, 177, 249, .65); color: #2b2b2b; padding-bottom: 2px;"><span style="width: 30px; height: 30px; display: block; background-image: url(https://files.mdnice.com/fullstack-2.png); background-position: center; background-size: 30px; margin: auto; opacity: 1; background-repeat: no-repeat; margin-bottom: -8px;"></span>前方高能</span><span class="suffix" style="display: none;"></span></h3>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">咱们今天讲的内容，可能和<strong style="color: #3594F7; font-weight: bold;"><span>「</span>前端<span>」</span></strong>关联比较大，如果没有经验的同学，或者说只想专注后端的同学，你们直接copy前端的代码即可。</p>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">秉着毁人不倦的思想，笔者打算细讲一部分前端的内容，毕竟还有一部分读者是<strong style="color: #3594F7; font-weight: bold;"><span>「</span>前后兼修<span>」</span></strong>的。</p>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">如果你们也想React相关知识，在我这里可能不能系统地学到，但是在 <strong style="color: #3594F7; font-weight: bold;"><span>「</span>测试平台(序)<span>」</span></strong> 中也介绍了相关的前端学习建议和地址，希望大家恶补一下，毕竟这方面也不算难。</p>
<h3 data-tool="mdnice编辑器" style="padding: 0px; color: black; font-size: 17px; font-weight: bold; text-align: center; position: relative; margin-top: 20px; margin-bottom: 20px;"><span class="prefix" style="display: none;"></span><span class="content" style="border-bottom: 2px solid RGBA(79, 177, 249, .65); color: #2b2b2b; padding-bottom: 2px;"><span style="width: 30px; height: 30px; display: block; background-image: url(https://files.mdnice.com/fullstack-2.png); background-position: center; background-size: 30px; margin: auto; opacity: 1; background-repeat: no-repeat; margin-bottom: -8px;"></span>编写登录接口</span><span class="suffix" style="display: none;"></span></h3>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">与注册接口类似，甚至更加简单，先理一下思路:</p>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">用户通过<strong style="color: #3594F7; font-weight: bold;"><span>「</span>页面<span>」</span></strong>(这里暂时还没有)输入<strong style="color: #3594F7; font-weight: bold;"><span>「</span>用户名<span>」</span></strong>和<strong style="color: #3594F7; font-weight: bold;"><span>「</span>密码<span>」</span></strong>，点击登录按钮进行登录，如果成功的话则将用户信息放入返回即可。如果用户名或者密码不正确，则给出提示，拒绝让用户登录！</p>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #3594F7; background: RGBA(59, 170, 250, .1); padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px;">Talk is cheap, show me the code!</code></p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">from</span>&nbsp;datetime&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">import</span>&nbsp;datetime<br><br><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">from</span>&nbsp;sqlalchemy&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">import</span>&nbsp;or_<br><br><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">from</span>&nbsp;app.middleware.Jwt&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">import</span>&nbsp;UserToken<br><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">from</span>&nbsp;app.models&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">import</span>&nbsp;db<br><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">from</span>&nbsp;app.models.user&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">import</span>&nbsp;User<br><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">from</span>&nbsp;app.utils.logger&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">import</span>&nbsp;Log<br><br><br><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #e6c07b; line-height: 26px;">UserDao</span><span class="hljs-params" style="line-height: 26px;">(object)</span>:</span><br>&nbsp;&nbsp;&nbsp;&nbsp;log&nbsp;=&nbsp;Log(<span class="hljs-string" style="color: #98c379; line-height: 26px;">"UserDao"</span>)<br><br><span class="hljs-meta" style="color: #61aeee; line-height: 26px;">&nbsp;&nbsp;&nbsp;&nbsp;@staticmethod</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">def</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">register_user</span><span class="hljs-params" style="line-height: 26px;">(username,&nbsp;name,&nbsp;password,&nbsp;email)</span>:</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #98c379; line-height: 26px;">"""<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:param&nbsp;username:&nbsp;用户名<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:param&nbsp;name:&nbsp;姓名<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:param&nbsp;password:&nbsp;密码<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:param&nbsp;email:&nbsp;邮箱<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:return:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"""</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">try</span>:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;users&nbsp;=&nbsp;User.query.filter(or_(User.username&nbsp;==&nbsp;username,&nbsp;User.email&nbsp;==&nbsp;email)).all()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;users:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">raise</span>&nbsp;Exception(<span class="hljs-string" style="color: #98c379; line-height: 26px;">"用户名或邮箱已存在"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">#&nbsp;注册的时候给密码加盐</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pwd&nbsp;=&nbsp;UserToken.add_salt(password)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;user&nbsp;=&nbsp;User(username,&nbsp;name,&nbsp;pwd,&nbsp;email)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;db.session.add(user)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;db.session.commit()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">except</span>&nbsp;Exception&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">as</span>&nbsp;e:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UserDao.log.error(<span class="hljs-string" style="color: #98c379; line-height: 26px;">f"用户注册失败:&nbsp;<span class="hljs-subst" style="color: #e06c75; line-height: 26px;">{str(e)}</span>"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>&nbsp;str(e)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>&nbsp;<span class="hljs-literal" style="color: #56b6c2; line-height: 26px;">None</span><br><br><span class="hljs-meta" style="color: #61aeee; line-height: 26px;">&nbsp;&nbsp;&nbsp;&nbsp;@staticmethod</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">def</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">login</span><span class="hljs-params" style="line-height: 26px;">(username,&nbsp;password)</span>:</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">try</span>:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pwd&nbsp;=&nbsp;UserToken.add_salt(password)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">#&nbsp;查询用户名/密码匹配且没有被删除的用户</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;user&nbsp;=&nbsp;User.query.filter_by(username=username,&nbsp;password=pwd,&nbsp;deleted_at=<span class="hljs-literal" style="color: #56b6c2; line-height: 26px;">None</span>).first()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;user&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">is</span>&nbsp;<span class="hljs-literal" style="color: #56b6c2; line-height: 26px;">None</span>:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>&nbsp;<span class="hljs-literal" style="color: #56b6c2; line-height: 26px;">None</span>,&nbsp;<span class="hljs-string" style="color: #98c379; line-height: 26px;">"用户名或密码错误"</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">#&nbsp;更新用户的最后登录时间</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;user.last_login_at&nbsp;=&nbsp;datetime.now()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;db.session.commit()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>&nbsp;user,&nbsp;<span class="hljs-literal" style="color: #56b6c2; line-height: 26px;">None</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">except</span>&nbsp;Exception&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">as</span>&nbsp;e:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UserDao.log.error(<span class="hljs-string" style="color: #98c379; line-height: 26px;">f"用户<span class="hljs-subst" style="color: #e06c75; line-height: 26px;">{username}</span>登录失败:&nbsp;<span class="hljs-subst" style="color: #e06c75; line-height: 26px;">{str(e)}</span>"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>&nbsp;<span class="hljs-literal" style="color: #56b6c2; line-height: 26px;">None</span>,&nbsp;str(e)<br><br></code></pre>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">简单解释一下，我们这边新建了一个login的方法，接受参数是<strong style="color: #3594F7; font-weight: bold;"><span>「</span>username<span>」</span></strong>和<strong style="color: #3594F7; font-weight: bold;"><span>「</span>password<span>」</span></strong>，接着我们通过orm筛选出第一条username与password匹配且没有被删除的用户。</p>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #3594F7; background: RGBA(59, 170, 250, .1); padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px;">注意: 如果这里没有这个用户的话，user变量会是None，所以我采用了判断None的方式</code></p>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">最后我们把该用户的<strong style="color: #3594F7; font-weight: bold;"><span>「</span>最后登录时间<span>」</span></strong>改成了当前时间。然后提交到了orm的session，这句话等同于执行sql。</p>
<h3 data-tool="mdnice编辑器" style="padding: 0px; color: black; font-size: 17px; font-weight: bold; text-align: center; position: relative; margin-top: 20px; margin-bottom: 20px;"><span class="prefix" style="display: none;"></span><span class="content" style="border-bottom: 2px solid RGBA(79, 177, 249, .65); color: #2b2b2b; padding-bottom: 2px;"><span style="width: 30px; height: 30px; display: block; background-image: url(https://files.mdnice.com/fullstack-2.png); background-position: center; background-size: 30px; margin: auto; opacity: 1; background-repeat: no-repeat; margin-bottom: -8px;"></span>思考</span><span class="suffix" style="display: none;"></span></h3>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">我们在上一个接口里面，注册完用户以后哈，是<strong style="color: #3594F7; font-weight: bold;"><span>「</span>没有返回User实例信息<span>」</span></strong>的，但是这个接口不同，用户登录以后，需要拿到用户的信息以及token给前端处理。</p>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">但是我们的用户实例并不是一个<strong style="color: #3594F7; font-weight: bold;"><span>「</span>JSON<span>」</span></strong>对象，所以我们要为它做一些处理。</p>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">接着我们编写pity/handler/factory.py，目的是为了转换orm对象为<strong style="color: #3594F7; font-weight: bold;"><span>「</span>dict<span>」</span></strong>。</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">from</span>&nbsp;datetime&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">import</span>&nbsp;datetime<br><br><br><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #e6c07b; line-height: 26px;">ResponseFactory</span><span class="hljs-params" style="line-height: 26px;">(object)</span>:</span><br><br><span class="hljs-meta" style="color: #61aeee; line-height: 26px;">&nbsp;&nbsp;&nbsp;&nbsp;@staticmethod</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">def</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">model_to_dict</span><span class="hljs-params" style="line-height: 26px;">(obj,&nbsp;*ignore:&nbsp;str)</span>:</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data&nbsp;=&nbsp;dict()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">for</span>&nbsp;c&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">in</span>&nbsp;obj.__table__.columns:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;c.name&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">in</span>&nbsp;ignore:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">#&nbsp;如果字段忽略,&nbsp;则不进行转换</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">continue</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;val&nbsp;=&nbsp;getattr(obj,&nbsp;c.name)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;isinstance(val,&nbsp;datetime):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data[c.name]&nbsp;=&nbsp;val.strftime(<span class="hljs-string" style="color: #98c379; line-height: 26px;">"%Y-%m-%d&nbsp;%H:%M:%S"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">else</span>:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data[c.name]&nbsp;=&nbsp;val<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>&nbsp;data<br><br><span class="hljs-meta" style="color: #61aeee; line-height: 26px;">&nbsp;&nbsp;&nbsp;&nbsp;@staticmethod</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">def</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">model_to_list</span><span class="hljs-params" style="line-height: 26px;">(data:&nbsp;list,&nbsp;*ignore:&nbsp;str)</span>:</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>&nbsp;[ResponseFactory.model_to_dict(x,&nbsp;*ignore)&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">for</span>&nbsp;x&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">in</span>&nbsp;data]<br><br></code></pre>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">解释一下2个方法，第一个是将User此类实例转换为dict。由于orm实例都继承了<strong style="color: #3594F7; font-weight: bold;"><span>「</span>db.Model<span>」</span></strong>类，所以他们都有__table__属性，也就有了相关字段的信息。</p>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">ignore参数是一个可变字符串参数，当我们遇到需要忽略掉的字段时，可以派上用场。</p>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #3594F7; background: RGBA(59, 170, 250, .1); padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px;">这里可以看到笔者针对datetime类对象做了特殊处理，这是因为JSON中没有对应的datetime格式，所以我们需要对他也进行转换。</code></p>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">剩下的字段，直接通过set key value的方式赋值给<strong style="color: #3594F7; font-weight: bold;"><span>「</span>data<span>」</span></strong>字典即可。</p>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;"><strong style="color: #3594F7; font-weight: bold;"><span>「</span>model_to_list<span>」</span></strong>也比较简单，属于一个列表生成式，对每条orm实例都进行了转换，最终返回一个<strong style="color: #3594F7; font-weight: bold;"><span>「</span>list(orm object)<span>」</span></strong></p>
<h3 data-tool="mdnice编辑器" style="padding: 0px; color: black; font-size: 17px; font-weight: bold; text-align: center; position: relative; margin-top: 20px; margin-bottom: 20px;"><span class="prefix" style="display: none;"></span><span class="content" style="border-bottom: 2px solid RGBA(79, 177, 249, .65); color: #2b2b2b; padding-bottom: 2px;"><span style="width: 30px; height: 30px; display: block; background-image: url(https://files.mdnice.com/fullstack-2.png); background-position: center; background-size: 30px; margin: auto; opacity: 1; background-repeat: no-repeat; margin-bottom: -8px;"></span>编写route方法</span><span class="suffix" style="display: none;"></span></h3>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">最后我们就需要编写路由方法了，在pity/controller/auth/user.py新增如下方法:</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-meta" style="color: #61aeee; line-height: 26px;">@auth.route("/login",&nbsp;methods=['POST'])</span><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">def</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">login</span><span class="hljs-params" style="line-height: 26px;">()</span>:</span><br>&nbsp;&nbsp;&nbsp;&nbsp;data&nbsp;=&nbsp;request.get_json()<br>&nbsp;&nbsp;&nbsp;&nbsp;username,&nbsp;password&nbsp;=&nbsp;data.get(<span class="hljs-string" style="color: #98c379; line-height: 26px;">"username"</span>),&nbsp;data.get(<span class="hljs-string" style="color: #98c379; line-height: 26px;">"password"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">not</span>&nbsp;username&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">or</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">not</span>&nbsp;password:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>&nbsp;jsonify(dict(code=<span class="hljs-number" style="color: #d19a66; line-height: 26px;">101</span>,&nbsp;msg=<span class="hljs-string" style="color: #98c379; line-height: 26px;">"用户名或密码不能为空"</span>))<br>&nbsp;&nbsp;&nbsp;&nbsp;user,&nbsp;err&nbsp;=&nbsp;UserDao.login(username,&nbsp;password)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;err&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">is</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">not</span>&nbsp;<span class="hljs-literal" style="color: #56b6c2; line-height: 26px;">None</span>:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>&nbsp;jsonify(dict(code=<span class="hljs-number" style="color: #d19a66; line-height: 26px;">110</span>,&nbsp;msg=err))<br>&nbsp;&nbsp;&nbsp;&nbsp;user&nbsp;=&nbsp;ResponseFactory.model_to_dict(user,&nbsp;<span class="hljs-string" style="color: #98c379; line-height: 26px;">"password"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;token&nbsp;=&nbsp;UserToken.get_token(user)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;err&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">is</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">not</span>&nbsp;<span class="hljs-literal" style="color: #56b6c2; line-height: 26px;">None</span>:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>&nbsp;jsonify(dict(code=<span class="hljs-number" style="color: #d19a66; line-height: 26px;">110</span>,&nbsp;msg=err))<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>&nbsp;jsonify(dict(code=<span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>,&nbsp;msg=<span class="hljs-string" style="color: #98c379; line-height: 26px;">"登录成功"</span>,&nbsp;data=dict(token=token,&nbsp;user=user)))<br></code></pre>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">这边还是先判断了username和password是否为空的情况，其次调用了<strong style="color: #3594F7; font-weight: bold;"><span>「</span>UserDao<span>」</span></strong>的<strong style="color: #3594F7; font-weight: bold;"><span>「</span>login<span>」</span></strong>方法，返回user和err信息。</p>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #3594F7; background: RGBA(59, 170, 250, .1); padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px;">这里还有一个细节就是，我把用户的password给隐藏了</code></p>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">user = ResponseFactory.model_to_dict(user, "password")`</p>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">接着通过UserToken.get_token成功将对象转换成token，拿到token后返回了这样的对象:</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; padding-top: 15px; background: #282c34; border-radius: 5px;">{<br>&nbsp;&nbsp;<span class="hljs-attr" style="color: #d19a66; line-height: 26px;">"code"</span>:&nbsp;<span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>,<br>&nbsp;&nbsp;<span class="hljs-attr" style="color: #d19a66; line-height: 26px;">"data"</span>:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-attr" style="color: #d19a66; line-height: 26px;">"user"</span>:&nbsp;{},<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-attr" style="color: #d19a66; line-height: 26px;">"token"</span>:&nbsp;<span class="hljs-string" style="color: #98c379; line-height: 26px;">""</span><br>&nbsp;&nbsp;}<br>}<br></code></pre>
<h3 data-tool="mdnice编辑器" style="padding: 0px; color: black; font-size: 17px; font-weight: bold; text-align: center; position: relative; margin-top: 20px; margin-bottom: 20px;"><span class="prefix" style="display: none;"></span><span class="content" style="border-bottom: 2px solid RGBA(79, 177, 249, .65); color: #2b2b2b; padding-bottom: 2px;"><span style="width: 30px; height: 30px; display: block; background-image: url(https://files.mdnice.com/fullstack-2.png); background-position: center; background-size: 30px; margin: auto; opacity: 1; background-repeat: no-repeat; margin-bottom: -8px;"></span>验证</span><span class="suffix" style="display: none;"></span></h3>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">依旧是熟悉的postman出场时间，简单地测试一下:</p>
<ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; font-size: 15px; color: #595959; list-style-type: circle;">
<li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; font-size: 14px; font-weight: normal; color: #595959;">密码错误的情形</section></li></ul>
<figure data-tool="mdnice编辑器" style="margin: 0; margin-top: 10px; margin-bottom: 10px; display: flex; flex-direction: column; justify-content: center; align-items: center;"><img src="https://gitee.com/woodywrx/picture/raw/master/2021-3-9/1615291571701-image.png" alt style="max-width: 100%; border-radius: 6px; display: block; margin: 20px auto; object-fit: contain; box-shadow: 2px 4px 7px #999;"></figure>
<ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; font-size: 15px; color: #595959; list-style-type: circle;">
<li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; font-size: 14px; font-weight: normal; color: #595959;">密码正确的返回</section></li></ul>
<figure data-tool="mdnice编辑器" style="margin: 0; margin-top: 10px; margin-bottom: 10px; display: flex; flex-direction: column; justify-content: center; align-items: center;"><img src="https://gitee.com/woodywrx/picture/raw/master/2021-3-9/1615291706380-image.png" alt style="max-width: 100%; border-radius: 6px; display: block; margin: 20px auto; object-fit: contain; box-shadow: 2px 4px 7px #999;"></figure>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">可以看到，最后登录时间也正常显示了，token也生成了，password也没有暴露。想起抖音上的一些文案:</p>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #3594F7; background: RGBA(59, 170, 250, .1); padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px;">婚期已定，父母满意，对我体贴.....</code></p>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">有一种未来可期的感觉。</p>
<h3 data-tool="mdnice编辑器" style="padding: 0px; color: black; font-size: 17px; font-weight: bold; text-align: center; position: relative; margin-top: 20px; margin-bottom: 20px;"><span class="prefix" style="display: none;"></span><span class="content" style="border-bottom: 2px solid RGBA(79, 177, 249, .65); color: #2b2b2b; padding-bottom: 2px;"><span style="width: 30px; height: 30px; display: block; background-image: url(https://files.mdnice.com/fullstack-2.png); background-position: center; background-size: 30px; margin: auto; opacity: 1; background-repeat: no-repeat; margin-bottom: -8px;"></span>求饶</span><span class="suffix" style="display: none;"></span></h3>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">今天好像<strong style="color: #3594F7; font-weight: bold;"><span>「</span>干货<span>」</span></strong>比较多，看来没有足够的时间留给前端联调了，会在下一节补充起来吧，至于名字就也不改了吧。</p>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">后端代码地址: <a href="https://github.com/wuranxu/pity" style="text-decoration: none; word-wrap: break-word; color: #40B8FA; font-weight: normal; border-bottom: 1px solid #3BAAFA;">https://github.com/wuranxu/pity</a></p>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">前端代码地址: <a href="https://github.com/wuranxu/pityWeb" style="text-decoration: none; word-wrap: break-word; color: #40B8FA; font-weight: normal; border-bottom: 1px solid #3BAAFA;">https://github.com/wuranxu/pityWeb</a></p>
<p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;"><strong style="color: #3594F7; font-weight: bold;"><span>「</span>觉得有用的话可以帮忙点个Star哦QAQ<span>」</span></strong></p>
</section>
                </div>
                
                    <div class="tag-container">
                        
                            <a href="https://woodywrx.gitee.io/blog/4acLnmAXU/" class="tag">
                                自动化测试平台
                            </a>
                        
                    </div>
                

                
                    <div class="next-post">
                        <div class="next">下一篇</div>
                        <a href="https://woodywrx.gitee.io/blog/post/ce-shi-ping-tai-xi-lie-7-gai-zao-zhu-ce-jie-kou/">
                            <h3 class="post-title">
                                测试平台系列(7) 改造注册接口
                            </h3>
                        </a>
                    </div>
                
                
                    <span id="/blog/post/ce-shi-ping-tai-xi-lie-8-yu-qian-duan-lian-diao-zhu-ce-deng-lu-jie-kou-part-1/"
                          class="leancloud_visitors" data-flag-title="测试平台系列(8) 与前端联调注册/登录接口(part 1)">
                <em class="post-meta-item-text">阅读量 </em>
                <i class="leancloud-visitors-count">0</i>
            </span>
                
                
                    

	<div id="vcomments" style="width: 100%;max-width:1000%;padding:2.5%"></div>



                

            </div>

        </div>
    </div>
</div>

<script src="https://unpkg.com/aos@next/dist/aos.js"></script>
<script type="application/javascript">

AOS.init();

var app = new Vue({
  el: '#app',
  data: {
    menuVisible: false,
  },
})

</script>






<script src='https://cdn.jsdelivr.net/npm/valine/dist/Valine.min.js'></script>
<script>
    new Valine({
        el: '#vcomments',
        appId: 'fT8wvEVNtx1cOcCQEs7rVwnV-gzGzoHsz',
        appKey: 'xV6aDHKSkLfP7u0cBRIzpmcy',
        avatar: '',
        pageSize: 5,
        recordIp: true,
        placeholder: 'Just Go Go',
        visitor: true,
    });
</script>
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
</body>
</html>
